from typing import Any

import pytest
from sqlalchemy import select
from sqlalchemy.exc import DBAPIError
from sqlalchemy.ext.asyncio import AsyncSession

from recruit.adapter.database.gateway.user_gateway import UserGateway
from recruit.adapter.database.models import User, Resume
from .fixtures.user import UserTest
from .utils import delete_user, check_attr_model, check_get_one


async def _create_user(
        session: AsyncSession,
        user_gateway: UserGateway,
        data: dict[str, Any]
) -> UserTest:
    user = await user_gateway.create(**data)
    user = UserTest(
        id=user.id,
        user_id=user.user_id,
        username=user.username
    )
    await session.commit()
    return user


async def _check_user(
        session: AsyncSession,
        data: dict[str, Any],
        is_resume: bool = False
) -> None:
    stmt = select(User).filter_by(user_id=data['user_id'])
    user = (await session.execute(stmt)).scalar_one_or_none()
    assert isinstance(user, User)
    check_attr_model(user, data)
    if is_resume:
        assert len(user.resume) > 0
        assert isinstance(user.resume[0], Resume)


@pytest.mark.parametrize(
    'data', [{
        'user_id': 123456789,
        'username': 'username'
    }]
)
async def test_create(
        data: dict[str, Any],
        get_resume: dict[str, Any],
        db_session: AsyncSession
) -> None:
    user_gateway = UserGateway(db_session)
    with pytest.raises(DBAPIError):
        await user_gateway.create(**data)
    data['user_id'] = str(data['user_id'])
    user = await user_gateway.create(**data)
    check_attr_model(user, data)
    if get_resume.get('user_id'):
        del get_resume['user_id']
    user.resume = [Resume(**get_resume)]
    await db_session.commit()
    await _check_user(db_session, data, is_resume=True)
    await delete_user(db_session, user.id)


@pytest.mark.parametrize(
    'data', [{
        'user_id': '123456789',
        'username': 'username'
    }]
)
async def test_check_user(
        data: dict[str, Any],
        db_session: AsyncSession,
        get_user: dict[str, Any]
) -> None:
    user_gateway = UserGateway(db_session)
    created_user = await _create_user(db_session, user_gateway, get_user)
    val = await user_gateway.check_user(id=created_user.id)
    assert val

    for key, value in data.items():
        val = await user_gateway.check_user(**{key: value})
        assert val
    await delete_user(db_session, created_user.id)
    val = await user_gateway.check_user(id=created_user.id)
    assert not val


@pytest.mark.parametrize(
    'keys', [
        ('id', 'user_id', 'username')
    ]
)
async def test_get_one(
        keys: tuple[str, ...],
        db_session: AsyncSession,
        get_user: dict[str, Any]
) -> None:
    user_gateway = UserGateway(db_session)
    created_user = await _create_user(db_session, user_gateway, get_user)
    user = await user_gateway.get_one(id=created_user.id)
    assert user is not None
    check_get_one(user, created_user, keys)
    await delete_user(db_session, created_user.id)


@pytest.mark.parametrize(
    'data', [{
        'user_id': '1234567891',
        'username': 'testusername'
    }]
)
async def test_update(
        data: dict[str, str],
        db_session: AsyncSession,
        get_user: dict[str, Any]
) -> None:
    user_gateway = UserGateway(db_session)
    created_user = await _create_user(db_session, user_gateway, get_user)
    user = await user_gateway.update(created_user.id, **data)
    assert user.id == created_user.id
    check_attr_model(user, data)
    await db_session.commit()
    await _check_user(db_session, data)
    await delete_user(db_session, created_user.id)
